import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * This class is used to handle commands that require creating 
 * a new bash shell process and entering a command.  Job
 * execution occur here as well checking the system's status 
 * @author David Beaver
 */
public class CommandHandler {
    private ServerConfig config;

    public CommandHandler(ServerConfig config){
        this.config = config;
    }

    public String execCommand(String command, String workingDir){
        String out = "";
        if(command.equals("system-status")){
            List<String> commandList = new ArrayList<String>();
            commandList.add("/bin/sh");
            commandList.add("-c");
            commandList.add("free -m");
            ProcessBuilder cgi = new ProcessBuilder(commandList); //create a new Process that will launch the CGI
            cgi.redirectErrorStream(true); //set the stderr to be output with the stdout of the process
            BufferedInputStream cgiOutput = null; //create BufferedInputStream for the CGI output
            try {
                Process cgiRun = cgi.start(); //start the CGI process
                System.out.println("EXEC: Checking status...");
                cgiOutput = new BufferedInputStream(cgiRun.getInputStream()); //retrieve the stdout of the process
                new Thread( new Receiver( cgiOutput ) ).start(); //start the Receiver thread to read the stdout of the process
                try
                {
                    cgiRun.waitFor(); //wait for the CGI to finish
                    cgiRun.getErrorStream().close(); //close stderr
                    while(Receiver.isDone == false){} //wait for the Receiver to finish reading CGI output
                    out = Receiver.output.getOutput(); //save the output
                    System.out.println(out);
                    return out.replaceAll("\n", "<br>");
                }catch ( InterruptedException e ){
                    Thread.currentThread().interrupt();
                }
            } catch (IOException ex) {
                System.out.println("couldn't execute command");
            }
        }else{
            List<String> commandList = new ArrayList<String>();
            commandList.add("/bin/sh");
            commandList.add("-c");
            commandList.add(command);
            //System.out.println(workingDir);
            File currDir = new File(workingDir);
            if(currDir.exists()){
                System.out.println("EXEC:\t[1]Changing directories to "+workingDir);
                System.out.println("EXEC:\t[2]Running command..."+command);
                ProcessBuilder newJob = new ProcessBuilder(commandList);
                newJob.directory(currDir);
                try {
                    Process cgiRun = newJob.start(); //start the CGI process
                    System.out.println("EXEC:\t[3]Success!");
                    return "SUCCESS";
                } catch (IOException ex) {
                    System.out.println("couldn't execute command");
                }
            }
        }
        return out; //return the CGI output
        
    }
}

class Buffer{
    public StringBuilder output;

    public Buffer(){
        this.output = new StringBuilder();
    }

    public String getOutput(){
        String output = this.output.toString();
        this.output = new StringBuilder();
        return output;
    }

    public synchronized void write(String line){
        this.output.append(line).append("\n");
    }
}

class Receiver implements Runnable
{
    // ------------------------------ FIELDS ------------------------------

    /**
     * stream to receive data from child
     */
    public static Buffer output = new Buffer(); //global Buffer with which to process CGI output
    public static boolean isDone = false; //global flag indicated whether the Reciever is finished
    private final InputStream is; //client output stream

    // -------------------------- PUBLIC INSTANCE  METHODS --------------------------

    /**
     * method invoked when Receiver thread started.  Reads data from child and displays in on System.out.
     */
    public void run()
    {
        try
            {
            final BufferedReader br = new BufferedReader( new InputStreamReader( is ), 50 /* keep small for testing */ ); //create a BufferedReader to read the CGI output
            String line;
            while ( ( line = br.readLine() ) != null ) //while there is still output
            {
                Receiver.output.write(line); //save the output in the Buffer
            }
            
            br.close(); //close the reader
        }
        catch ( IOException e )
        {
            throw new IllegalArgumentException( "IOException receiving data from child process." );
        }
        //CGIHandler.done = true;
        //Receiver.output = new Buffer();
        Receiver.isDone = true; //signal that the Reciever is finished
    }

    // --------------------------- CONSTRUCTORS ---------------------------

    /**
     * contructor
     *
     * @param is stream to receive data from child
     */
    Receiver( InputStream is )
    {
        this.is = is;
    }
}